﻿using System;
using System.Linq;
using Sitecore.Links;
using Sitecore.Publishing.Pipelines.PublishItem;
using Sitecore.Sites;

namespace RedirectManager.Pipelines.PublishItem
{
    public enum ExistsResponse
    {
        /// <summary>
        /// Redirect path does not already exist
        /// </summary>
        NotFound,

        /// <summary>
        /// Redirect path / item combination already exists
        /// </summary>
        Found,

        /// <summary>
        /// Redirect path exist but with different target
        /// </summary>
        FoundDifferent
    }

    /// <summary>
    /// Hooks into publishItem event to add a record of the published url path if it doesn't already exist.
    /// </summary>
    public class Processor : PublishItemProcessor
    {
        #region Overrides of PublishItemProcessor

        // Adds the url *path* of each published item to the Redirects data store as the item is published.
        public override void Process(PublishItemContext context)
        {
            var options = LinkManager.GetDefaultUrlOptions();
            options.AlwaysIncludeServerUrl = true;
            options.SiteResolving = true;

            // Redirects are always presumed to not include a language prefix (otherwise we would need one per language)
            options.LanguageEmbedding = LanguageEmbedding.Never;

            // get target item
            var item = context.PublishOptions.TargetDatabase.GetItem(context.ItemId);
            if (item == null)
            {
                return;
            }

            // Iterate sites and create redirect record paths for each 
            foreach (var site in Sitecore.Configuration.Settings.Sites)
            {
                // skip ignored sites
                if (!RedirectManager.Config.IgnoredSites.Contains(site.Name, StringComparer.OrdinalIgnoreCase))
                {
                    // check item is navigable, i.e. below the site root.
                    if (item.Paths.ContentPath.StartsWith(site.StartItem, StringComparison.OrdinalIgnoreCase))
                    {
                        options.Site = SiteContextFactory.GetSiteContext(site.Name);
                        var link = LinkManager.GetItemUrl(context.PublishOptions.TargetDatabase.GetItem(context.ItemId), options);

                        // Fixes issue when hostname is specified and linkmanager generates links without a scheme - i.e. ://domain.com/blah.aspx
                        // Also be fixed by specifying scheme="http" attribute in the site definition.
                        if (link.StartsWith("://"))
                        {
                            link = "http" + link;
                        }

                        // Create as Uri object
                        string url;
                        try
                        {
                            var uri = new Uri(link);
                            url = uri.LocalPath; // using LocalPath instead of AbsolutePath to avoid unwanted escaping
                        }
                        catch (UriFormatException ex)
                        {
                            throw new ApplicationException(
                                string.Format(
                                    "Redirect Manager failed parsing item url generated by linkmanager. Url : {0} ItemId : {1}, ",
                                    link,
                                    context.ItemId),
                                ex);
                        }

                        // check if redirect record already exists
                        // TODO: Evaluate when to reassign existing redirect path to a different target item
                        // TODO: Exists function is probably not required - Create can be called and exceptions thrown instead.
                        var exists = HttpRequest.Redirector.Provider.Exists(url);
                        if (!exists)
                        {
                            // Create new redirect record
                            HttpRequest.Redirector.Provider.Create(url, context.ItemId.ToString(), true);
                        }
                        else
                        {
                            // Overwrite existing record with new target
                            HttpRequest.Redirector.Provider.Delete(url);
                            HttpRequest.Redirector.Provider.Create(url, context.ItemId.ToString(), true);
                        }

                        // Redirect with same path and target id already exists. Do nothing
                    }
                }
            }
        }

        #endregion
    }
}
